package com.stars.easyms.datasource;

import com.stars.easyms.datasource.support.DataSourceSupport;
import com.stars.easyms.base.util.StringFormatUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.datasource.AbstractDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * <p>className: EasyMsDataSource</p>
 * <p>description: EasyMs的封装数据源</p>
 *
 * @author guoguifang
 * @version 1.2.1
 * @date 2019/5/14 13:09
 */
@Slf4j
@SuppressWarnings("unchecked")
public final class EasyMsDataSource extends AbstractDataSource {

    @Getter
    private String url;

    @Getter
    private String abbrUrl;

    private String username;

    private String password;

    private DataSourceSupport dataSourceSupport;

    private DataSource dataSource;

    private final AtomicLong currentExecuteCount = new AtomicLong();

    private final AtomicLong executedCount = new AtomicLong();

    private final Map<Long, SqlExecuteContent> currentExecuteSqlMap = new ConcurrentHashMap<>(256);

    EasyMsDataSource(DataSourceSupport dataSourceSupport, DataSource dataSource) {
        this.dataSourceSupport = dataSourceSupport;
        this.dataSource = dataSource;
    }

    @Override
    public Connection getConnection() throws SQLException {
        try {
            return this.dataSource.getConnection();
        } catch (SQLException e) {
            log.error("DataSource[{}] get connection fail!\r\n### Connection param: {}\r\n### CurrentExecuteCount: {}, ExecutedCount: {}",
                    abbrUrl, StringFormatUtil.formatBlank(this.dataSource.toString()), getCurrentExecuteCount(), getExecutedCount());
            throw e;
        }
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        try {
            return this.dataSource.getConnection(username, password);
        } catch (SQLException e) {
            log.error("DataSource[{}] get connection fail!\r\n### Connection param: {}\r\n### CurrentExecuteCount: {}, ExecutedCount: {}",
                    abbrUrl, StringFormatUtil.formatBlank(this.dataSource.toString()), getCurrentExecuteCount(), getExecutedCount());
            throw e;
        }
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        if (iface.isInstance(this)) {
            return (T) this;
        }
        return this.dataSource.unwrap(iface);
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return (iface.isInstance(this) || this.dataSource.isWrapperFor(iface));
    }

    public boolean isValid() {
        return this.dataSourceSupport.isValid(dataSource);
    }

    void setConnectParams(String url, String username, String password) {
        if (StringUtils.isBlank(url) || StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
            throw new IllegalArgumentException("DataSource parameters 'url' 'username' 'password' not be blank!");
        }
        this.url = url;
        this.abbrUrl = convertUrlToAbbr(url);
        this.username = username;
        this.password = password;
        this.dataSourceSupport.setConnectParams(dataSource, url, username, password);
    }

    void increase(SqlExecuteContent sqlExecuteContent) {
        currentExecuteCount.incrementAndGet();
        currentExecuteSqlMap.put(sqlExecuteContent.getSeq(), sqlExecuteContent);
    }

    void decrease(Long executeSeq) {
        currentExecuteCount.decrementAndGet();
        currentExecuteSqlMap.remove(executeSeq);
        executedCount.incrementAndGet();
    }

    EasyMsDataSource cloneDataSource() {
        return new EasyMsDataSource(dataSourceSupport, dataSourceSupport.cloneDataSource(this.dataSource));
    }

    public DataSource getDataSource() {
        return this.dataSource;
    }

    public List<SqlExecuteContent> getCurrentExecuteSqlList() {
        return new ArrayList<>(this.currentExecuteSqlMap.values());
    }

    public long getCurrentExecuteCount() {
        return this.currentExecuteCount.get();
    }

    public long getExecutedCount() {
        return this.executedCount.get();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || obj.getClass() != this.getClass() || this.url == null || this.username == null || this.password == null) {
            return false;
        }
        EasyMsDataSource easyMsDataSource = (EasyMsDataSource) obj;
        return this.url.equals(easyMsDataSource.url) && this.username.equals(easyMsDataSource.username) && this.password.equals(easyMsDataSource.password);
    }

    @Override
    public int hashCode() {
        int result = 59 + (this.url == null ? 43 : this.url.hashCode());
        result = result * 59 + (this.username == null ? 43 : this.username.hashCode());
        return result * 59 + (this.password == null ? 43 : this.password.hashCode());
    }

    private String convertUrlToAbbr(String url) {
        int index = url.indexOf('?');
        return index == -1 ? url : url.substring(0, index);
    }

}